package cwinsor.com.leaf_raking;


import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.FileReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;

import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Random;


/**
 * PrsnSvcs
 * Person List - a base class that has an array of persons
 * offers a variety of methods on that list
 *
 */
abstract public class PrsnList {

	// the list
	ArrayList<Person> personList;


	/**
	 * constructor
	 */
	public PrsnList() {
		personList = new ArrayList<Person>();
	}

	/* 
	 * add to the list
	 */
	public void addPerson(Person person) {
		personList.add(person);
	}

	/*
	 * get the nth person in the list
	 */
	public Person getPerson(int n) {
		return personList.get(n);
	}

	/*
	 * get the list
	 */
	public ArrayList<Person> getPersonList() {
		return personList;
	}

	/*
	 * count - returns the number of persons in the list
	 */
	public int getCount() {
		return personList.size();
	}


	/*
	 * for derived class - add a person using array of fields
	 */
	//	public abstract Person doAddPerson(ArrayList<String> theFieldsArrayList, FileType fT);


	/*
	 * read in data from a
	 * roster file (.csv delimited)
	 * or a
	 * state file (.csv delimited)
	 */
	public void populateFromFile(FileType fT, File fileName) {

		System.out.println(dS + ": reading " + fT.getName() + " file " + fileName);

		BufferedReader br = null;
		String line = "";
		String cvsSplitBy = ",";

		try {

			br = new BufferedReader(new FileReader(fileName));

			while ((line = br.readLine()) != null) {

				// read data into a string array, use comma as separator
				String[] theFields = line.split(cvsSplitBy);

				// convert to an ArrayList object
				ArrayList<String> theFieldsArrayObject = new ArrayList<String>();
				for (String s : theFields) {
					theFieldsArrayObject.add(s);
				}

				if (theFields.length == 0) {
					Msg.note(dS + ": WARNING - there is an empty line in the input file - it is ignored");
				}
				else {

					// create a list of persons (empty)
					ArrayList<Person> persons = new ArrayList<Person>();

					// get person(s) from the file
					doPopulateFromFile(fT, theFieldsArrayObject, persons);

					// add person(s) to our person list
					for (Person p : persons) {
						personList.add(p);
					}
				}
			}
			System.out.println(dS + "there are " + personList.size() + " persons");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

		System.out.println(dS + ": reading csv file DONE");
		// check
		// this.doCheck();
	}


	/*
	 * derived class will populate either rakers or supervisors
	 */
	abstract public void doPopulateFromFile(
			FileType fT,
			ArrayList<String> theFieldsArrayObject,
			ArrayList<Person> persons);



	public void writeToFile(FileType fT, PrintWriter writer) {
		//	System.out.println(dS + " writing " + fT.getName());

		Iterator<Person> iterator = personList.iterator();
		while (iterator.hasNext()) {
			Person person = iterator.next();

			// write it to file
			doWriteToFile(fT, person, Person.Format.LONG_FORM, writer);
		}
	}


	public void doWriteToFile(
			FileType fT,
			Person person,
			Person.Format format,
			PrintWriter writer
			) {

		fT.writeToFile(person, writer, format);
	}


	/*
	 * apply schedule preference updates
	 */
	public void applySchedulePreferencesFromFile(FileType fT, File fileName) {

		System.out.println(dS + ": applying updated schedule preferences " + fT.getName() + " file " + fileName);

		BufferedReader br = null;
		String line = "";
		String cvsSplitBy = ",";

		try {

			br = new BufferedReader(new FileReader(fileName));

			while ((line = br.readLine()) != null) {

				 Msg.note(dS + ": looking at \"" + line + "\"");

				// read data into a string array, use comma as separator
				String[] theFields = line.split(cvsSplitBy);

				// convert to an ArrayList object
				ArrayList<String> theFieldsArrayObject = new ArrayList<String>();
				for (String s : theFields) {
					theFieldsArrayObject.add(s);
				}

				if (theFields.length == 0) {
					Msg.note(dS + ": WARNING - there is an empty line in the input file - it is ignored");
				}
				else {

					// apply the schedule preferences
					doApplySchedulePreferencesFromFile(fT, theFieldsArrayObject, this);

				}
			}
			System.out.println(dS + "there are " + personList.size() + " persons");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

		System.out.println(dS + ": reading csv file DONE");
		// check
		// this.doCheck();
	}


	/*
	 * derived class will update either rakers or supervisors
	 */
	abstract public void doApplySchedulePreferencesFromFile(
			FileType fT,
			ArrayList<String> theFieldsArrayObject,
			PrsnList personList);


	/*
	 * this method applies the Add/Drops to the current schedule, and clears the Add/Drop list
	 */
	public void acceptAddDrops() {
		Iterator<Person> iterator = personList.iterator();
		while (iterator.hasNext()) {
			Person p = iterator.next();
			p.applyAddDrops();
		}
	}

	/*
	 * print the data to screen
	 */
	//	public void print(Person.Format fmt) {
	//		Iterator<Person> iterator = personList.iterator();
	//		while (iterator.hasNext()) {
	//			Person p = iterator.next();
	//			System.out.println(p.toStringCommaDelimitedAllFields(fmt));
	//		}
	//	}

	/*
	 * return a String of all contents in commma-separated-value format
	 */
	//	public String toCsvString(String in, Person.Format fmt) {
	//
	//		// debug print
	//		if (debugSection == 3) {
	//			System.out.println("---- toCsvString...  list.size = " + personList.size());
	//		}
	//		Iterator<Person> iterator = personList.iterator();
	//		while (iterator.hasNext()) {
	//			Person p = iterator.next();
	//
	//			in = in + p.toStringCommaDelimitedAllFields(fmt);
	//			in = in + "\n";
	//		}
	//		// debug print
	//		if (debugSection == 3) {
	//			System.out.println("---- toCsvString...  done");
	//		}
	//		return in;
	//	}


	/*
	 * derived class - construct one of yourself
	 */
	public abstract PrsnList doConstruct();


	/*
	 * return a list of Persons given team, shift
	 */
	public PrsnList getPersonsDayShiftTeam(WorkShift workShift, TeamNumber teamNumber) {
		PrsnList theList = doConstruct();

		// for each entry in the list
		for (Person person : getPersonList()) {
			if (person.getAssignmentCurrent(workShift).equals(teamNumber)) {
				theList.addPerson(person); 
			}
		}
		return theList;	
	}


	//	public void takeInUpdates(PrsnList updatesList) {
	//		Iterator<Person> iterator = updatesList.getPersonList().iterator();
	//		while (iterator.hasNext()) {
	//			Person pUpdate = iterator.next();
	//
	//			// find the equivalent person in the main list
	//			Person pMain = findPerson(pUpdate);
	//			if (pMain == null) {
	//				Msg.fatal(dS + ": when taking in updates could not find person " + pUpdate.getFirstName() + " " + pUpdate.getLastName() + " in the main list");
	//			}
	//			pMain.takeInUpdates(pUpdate);
	//		}
	//	}

	/*
	 * find person in the list
	 * comparison is done using first and last name
	 */
	public Person findPerson(Person thatPerson) {
		// for each entry in the list
		for (Person person : getPersonList()) {
			if (person.equals(thatPerson)) {
				return person;
			}
		}
		return null;
	}

	/*
	 * find person given first and last name
	 */
	public Person findPerson(String firstName, String lastName) {
		// for each entry in the list
		for (Person person : personList) {
			if (person.getFirstName().equals(firstName) &&
					person.getLastName().equals(lastName)) {
				return person;
			}
		}
		return null;
	}

	/*
	 * find a person given first initial, last name
	 * returns a list of people
	 */
	public void findPersonCommon(String first, String last,	ArrayList<Person> matchingPersons) {
		if (first.substring(1, 1).equals(".")) {
			findPerson2(first,last,matchingPersons);
		} else {
			findPerson3(first,last,matchingPersons);	
		}
	}

	/*
	 * find a person given first initial, last name
	 * returns a list of people
	 */
	public void findPerson2(String firstInitial, String lastName,
			ArrayList<Person> matchingPersons) {
		// for each entry in the list
		for (Person person : personList) {
			if (person.getFirstInitial().equals(firstInitial) &&
					person.getLastName().equals(lastName)) {
				matchingPersons.add(person);
			}
		}
	}


	/*
	 * find a person given first name, last name
	 * returns a list of people
	 */
	public void findPerson3(String firstName, String lastName,
			ArrayList<Person> matchingPersons) {
		// for each entry in the list
		for (Person person : personList) {
			if (person.getFirstName().equals(firstName) &&
					person.getLastName().equals(lastName)) {
				matchingPersons.add(person);
			}
		}
	}

	/*
	 * find a person given last name only
	 * returns a list of people
	 */
	public void findPerson4(String lastName,
			ArrayList<Person> matchingPersons) {
		// for each entry in the list
		for (Person person : personList) {
			if (person.getLastName().equals(lastName)) {
				matchingPersons.add(person);
			}
		}
	}

	/*
	 * print the data to screen
	 */
	public void doCheck() {
		System.out.println(dS + "checking... ");

		if (debugSection == 5) {
			for (WorkShift s: WorkShift.asList()) {
				System.out.println(dS
						+ s.name());
			}
		}
		for (WorkShift workShift: WorkShift.asList()) {
			for (TeamNumber teamNumber: TeamNumber.TEAMS_NO_U) {

				// check that the number of persons == 3
				PrsnList r = getPersonsDayShiftTeam(workShift,teamNumber);
				if (r.getCount() != 3) {
					System.out.println("          NOTE --- "
							+ workShift.day().name()
							+ " " + workShift.name()
							+ " " + teamNumber.name()
							+ " has "
							+ r.getCount() + " persons !!!");
				} else {
					System.out.println("          NOTE --- "
							+ workShift.day().name()
							+ " " + workShift.name()
							+ " " + teamNumber.name()
							+ " has "
							+ r.getCount() + " persons");
				}
			}
			System.out.println("");
		}
		System.out.println(dS + "checking done ");
	}


	/*
	 * print to screen just the degree of change
	 */
	//	public void printSummaryOfChange() {
	//		// for each entry in the list
	//		for (Person person : getPersonList()) {
	//			System.out.println(person.toStringCommaDelimitedAllFields(Person.Format.CHANGE_SUMMARY));
	//		}
	//	}


	// debug
	String dS = this.getClass().getSimpleName();
	Integer dL = new Integer(1);
	Integer debugSection = new Integer(0);
}